package com.scs.application.core.socket;

import com.alibaba.fastjson.JSON;
import com.scs.application.core.dto.WebSocketMsgDTO;
import com.scs.application.core.utils.UtilWebsocket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * socket服务端
 * 标注为端点：@ServerEndpoint，其中"/spd/{stationCode}"为访问路径
 */
@ServerEndpoint("/spdsocket")
@Component
@Slf4j
public class SpdWebSocketServerService {
    /**
     * 存储所有存活的用户
     * 注意1：高并发问题
     * 注意2：livingSessions必须是全局变量（保证全局就他一个变量，否则每次调用都会被刷新）
     * 注意3：很难保证，客户端在退出时，正确调用了WebSocket.close()，也就是调用后端的onClose()方法，这样
     * 就可能会导致Session无法被有效清除，livingSessions会越来越大，服务器压力也会越来越大。
     * 所以，我们需要周期性的去检查用户是否还处于活跃状态，不活跃的，移除该用户的session
     */
    public static Map<String, Session> livingSessions = new ConcurrentHashMap<String, Session>();

    /**
     * 前端一旦启用WebSocket,机会调用@OnOpen注解标注的方法
     *
     * @param session  会话，每个访问对象都会有一个单独的会话
     */
    @OnOpen
    public void openSession( Session session) {
        log.debug("openSession");
        livingSessions.put(session.getId(), session);
        UtilWebsocket.setCache();
    }

    /**
     * 服务端发送消息给前端时调用
     *
     * @param session  会话，每个访问对象都会有一个单独的会话
     * @param message  待发送的消息
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        log.debug("onMessage");
    }

    /**
     * 客户端关闭WebSocket连接时，调用标注@OnClose的方法
     *
     * @param stationCode 路径参数
     * @param session  会话，每个访问对象都会有一个单独的会话
     */
    @OnClose
    public void onClose(Session session) {
        //将当前用户移除
        log.debug("onClose");
        livingSessions.remove(session.getId());
        UtilWebsocket.setCache();
    }
    /**
     * 发生错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.debug("onError");
    }

    /**
     * 遍历所有存活的用户，并发送消息（PS：就是广播消息）
     */
    public void sendTextAll(WebSocketMsgDTO webSocketMsg) {
        livingSessions.forEach((sessionId, session) -> {
            //发送消息对象
            RemoteEndpoint.Basic basicRemote = session.getBasicRemote();
            try {
                //发送消息
                basicRemote.sendText(JSON.toJSONString(webSocketMsg));
            } catch (Exception e) {
                log.error("sendTextAll.error",e);
            }
        });
    }
}
